<?php

namespace W3;

use Base\Users;
use W3\Exception;
use function action_exists;
use function do_action;

!defined('W3_ROOT_DIR') AND exit;

/**
 * 当前登录用户
 *
 * @author edikud
 * @date 2022/10/22
 * @copyright Copyright (c) 2022 W3 (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */
class Auth extends Users
{
    /**
     * 用户组
     *
     * @access public
     * @var array
     */
    public $groups = [];

    /**
     * 是否已经登录
     *
     * @access private
     * @var boolean
     */
    protected $hasLogin = NULL;

    /**
     * 缓存
     * @var array
     */
    protected $current = [];

    /**
     * @param int $components
     */
    protected function initComponents(int &$components)
    {
        $components = self::INIT_CONFIG;
    }

    /**
     * 初始化
     *
     * @access protected
     * @return void
     */
    protected function init()
    {
		# 定义变量默认数据
        $this->parameter([
			
			# 设置主体
			'main' => 'auth',	
			
		], true);

        $this->groups = [
            'admin'         => [ 'name'=>__('管理员'), 'level'=>0 ],
            'editor'		=> [ 'name'=>__('编辑员'), 'level'=>1 ],
            'contributor'	=> [ 'name'=>__('贡献者'), 'level'=>2 ],
            'member'	    => [ 'name'=>__('注册者'), 'level'=>3 ],
            'visitor'		=> [ 'name'=>__('访问者'), 'level'=>4 ],
		];
		
        if ($this->hasLogin()) {
			
			$this->push($this->current);
			
            # 更新最后活动时间
           ($this->config->time - $this->activated) > 100 && $this->db
		        ->update('table.users')
                ->where('table.users.uid = ?', $this->uid)
                ->rows(['activated' => $this->config->time])
			    ->affected();
			
            # 合并个人选项
            $options = $this->options->export();
            foreach ($options as $key => $val) 
			{
                $this->config->{$key} = $val;
            }
        }
	}

    /**
     * 获取用户组
     * @param  [type] $group
     * @return [type]       
     */
    public function group(?string $group = NULL): string
    {
        return $this->groups[$group]['name'] ?? $this->groups[$this->group]['name'];
    }

    /**
     * 检查当前用户是否为游客
     *
     * @access public
     * @return boolean
     */
    public function hasLogin(): ?bool
    {
        if (NULL !== $this->hasLogin) {
            return $this->hasLogin;
        } else {
            $uid = Cookie::get('__w3_uid');
            if (NULL !== $uid) {
				
                /** 验证登陆 */
                $user = $this->db
				    ->select()
				    ->from('table.users')
                    ->where('uid = ?', intval($uid))
                    ->limit(1)
					->fetch();

                $salt = Cookie::get('__w3_salt');
                if ($user && Util::hashValidate($user['salt'], $salt)) {
                    $this->current = $user;
                    return $this->hasLogin = true;
                }

                $this->logout();
            }

            return $this->hasLogin = false;
        }
    }

    /**
     * 检查当前用户是否为管理员
     *
     * @return bool
     * @throws Exception
     */
    public function admin(): bool
    {
        return $this->check('admin', true);
    }

    /**
     * 当前用户
     *
     * @return []
     * @throws Exception
     */
    public function user($name = NULL, $default = null)
    {
        return NULL !== $name
		    ? ($this->current[$name] ?? $default)
			: $this->current;
    }

    /**
     * 判断用户权限
     *
     * @access public
     * @param string $group 用户组
     * @param boolean $return 是否为返回模式
     * @return boolean
     */
    public function check(string $group, bool $return = false): bool
    {
        if ($this->hasLogin()) {
            if (array_key_exists($group, $this->groups) && $this->groups[$this->group]['level'] <= $this->groups[$group]['level']) {
                return true;
            }
        } else {
            if ($return) {
                return false;
            } else {

                // 防止循环重定向
                $this->redirect($this->loginUrl(false) .
                (0 === strpos($this->request->referer(), $this->loginUrl(false)) ? '' :
                '?referer=' . urlencode($this->request->makeUrl())), 302);
            }
        }

        if ($return) {
            return false;
        } else {
			
			throw new Exception(__('禁止访问'), 403);
        }
    }
	
    /**
     * 以用户名和密码登录
     *
     * @access public
     * @param string $name 用户名或邮箱
     * @param string $password 密码
     * @param boolean $temporarily 是否为临时登录
     * @param integer $expire 过期时间
     * @return boolean
     */
    public function login(string $name, string $password, bool $temporarily = false, int $expire = 0): bool
    {
		/** 插件接口 */
        if (action_exists('login')) {
            return do_action('login', $this, false, $name, $password, $temporarily, $expire);
        }

        /** 开始验证用户 **/
        $user = $this->db
			->select()
		    ->from('table.users')
            ->where((strpos($name, '@') ? 'mail' : 'name') . ' = ?', $name)
			->limit(1)
			->fetch();

        if (empty($user)) {
            return false;
        }
	
        if (action_exists('hashvalidate')) {
			
			/** 插件接口 */
            $hashValidate = do_action('hashvalidate', $this, false, $password, $user['password']);
        } else {
			$hashValidate = Util::hashValidate($password, $user['password']);
		}

        if ($hashValidate) {
            if (!$temporarily) {
                $this->commitLogin($user, $expire);
            }

            $this->current = $user;
            $this->hasLogin = true;

			/** 插件接口 */
			do_action('login_success', $this, false, $name, $password, $temporarily, $expire);
			
            return true;
        }
		
		/** 插件接口 */
		do_action('login_fail', $this, false, $name, $password, $temporarily, $expire);

        return false;
    }

    /**
     * 只需要提供uid或者完整user数组即可登录的方法, 多用于插件等特殊场合
     *
     * @access public
     * @param int | array $uid 用户id或者用户数据数组
     * @param boolean $temporarily 是否为临时登录，默认为临时登录以兼容以前的方法
     * @param integer $expire 过期时间
     * @return boolean
     */
    public function simpleLogin($uid, bool $temporarily = true, int $expire = 0): bool
    {
        if (is_array($uid)) {
            $user = $uid;
        } else {
            $user = $this->db
			    ->select()
		        ->from('table.users')
                ->where('uid = ?', $uid)
                ->limit(1)
			    ->fetch();
        }

        if (empty($user)) {
			
			do_action('simple_login_fail', $this, false, $uid, $temporarily, $expire);

            return false;
        }

        if (!$temporarily) {
            $this->commitLogin($user, $expire);
        }

        $this->current = $user;
        $this->hasLogin = true;
		
		do_action('simple_login_success', $this, false, $user);

        return true;
    }

    /**
     * @param $user
     * @param int $expire
     */
    public function commitLogin(&$user, int $expire = 0)
    {
        $salt = sha1(Util::randstring(20));
        $user['salt'] = $salt;

        Cookie::set('__w3_uid', $user['uid'], $expire);
        Cookie::set('__w3_salt', Util::hash($salt), $expire);

        # 更新验证码 更新最后登录时间
        $this->db
		    ->update('table.users')
            ->where('uid = ?', $user['uid'])
            ->rows(['salt' => $salt, 'logged' => $this->config->time])
			->affected();
    }

    /**
     * 用户登出函数
     *
     * @access public
     * @return void
     */
    public function logout()
    {
        if (action_exists('logout')) {
			
		    /** 插件接口 */
		    do_action('logout');
            return;
        }

        Cookie::delete('__w3_uid');
        Cookie::delete('__w3_salt');
    }
	
    /**
     * 获取用户组名称
     * @param  [type] $group
     * @return [type]       
     */
    protected function ___groupNames(): array
    {
		$groups = [];
        foreach ($this->groups as $key => $val) 
	    {
            $groups[$key] = $val['name'];
        }

        return $groups;
    }
}